home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / byteibm.arc / PM_AT.ASM < prev    next >
Assembly Source File  |  1985-07-12  |  34KB  |  1,366 lines

  1.         NAME    PM_AT
  2.         PAGE    60, 132
  3.         .286C
  4. ;
  5. ;    PM/AT - A program to place the PC/AT into Protected Mode
  6. ;    Copyright 1985, Ross P. Nelson
  7. ;
  8.  
  9. INCLUDE \usr\include\protect.inc
  10.  
  11. ; Data structure definitions
  12. DESCRIP     STRUC                ; generic descriptor format
  13.   limit     DW    ?            ; offset if gate
  14.   phys_addr_lo    DW    ?            ; selector if gate
  15.   phys_addr_hi    DB    ?            ; wc if gate
  16.   access    DB    ?            ; access rights
  17.         DW    0            ; reserved for 386
  18. DESCRIP     ENDS
  19.  
  20. TSS_BLOCK    STRUC                ; format of a TSS
  21.   back_link    DW    ?            ; previously active TSS
  22.   rSP0        DW    ?            ; level 0 stack
  23.   rSS0        DW    ?
  24.   rSP1        DW    ?            ; level 1 stack
  25.   rSS1        DW    ?
  26.   rSP2        DW    ?            ; level 2 stack
  27.   rSS2        DW    ?
  28.   rIP        DW    ?
  29.   FLAGS     DW    ?
  30.   rAX        DW    ?
  31.   rCX        DW    ?
  32.   rDX        DW    ?
  33.   rBX        DW    ?
  34.   rSP        DW    ?
  35.   rBP        DW    ?
  36.   rSI        DW    ?
  37.   rDI        DW    ?
  38.   rES        DW    ?
  39.   rCS        DW    ?
  40.   rSS        DW    ?            ; active stack segment
  41.   rDS        DW    ?
  42.   task_LDT    DW    ?            ; LDT selector
  43. TSS_BLOCK    ENDS
  44.  
  45. ; Literal values for descriptor types
  46. TSS        EQU    1
  47. LDT        EQU    2
  48. TSS_BUSY    EQU    3
  49. CALL_GATE    EQU    4
  50. TASK_GATE    EQU    5
  51. INT_GATE    EQU    6
  52. TRAP_GATE    EQU    7
  53.  
  54. RDONLY        EQU    0            ; read only
  55. RD_WR        EQU    1            ; read/write
  56. RD_WR_XD    EQU    3            ; read/write expand down
  57. EXONLY        EQU    4            ; execute only
  58. EX_RD        EQU    5            ; execute/readable
  59. EXONLY_CF    EQU    6            ; execute only/conforming
  60. EX_RD_CF    EQU    7            ; execute/readable/conforming
  61.  
  62. TSS_LIMIT    EQU    43
  63.  
  64. ; Segment building macros
  65. MSEG        MACRO    name,type,priv,combine    ;; start a memory segment
  66. name        SEGMENT PARA combine        ;; MASM directive
  67. zero = $                    ;; for ALIGN macro
  68. &name&_start = $                ;; origin
  69. &name&_ar = 90h OR (priv SHL 5) OR (type SHL 1) ;; access rights
  70.         ENDM
  71.  
  72. SSEG        MACRO    name,type,priv        ;; start a system segment
  73. name        SEGMENT PARA            ;; MASM directive
  74. zero = $                    ;; for ALIGN macro
  75. &name&_start = $                ;; origin
  76. &name&_ar = 80h OR (priv SHL 5) OR type     ;; access rights
  77.         ENDM
  78.  
  79. ENDSEG        MACRO    name            ;; terminate a segment
  80. &name&_limit = $ - &name&_start - 1        ;; create variable for seg limit
  81. name        ENDS                ;; limit <- size-1  (0-FFFFh)
  82.         ENDM
  83.  
  84. ; Descriptor building macros
  85. DSCRP        MACRO    export,name        ;; build descrip for segment
  86.         IFDIF    <export>,<>        ;; check for export name
  87. export        LABEL    WORD
  88.         ENDIF
  89.         DW    &name&_limit        ;; segment limit
  90.         DW    name            ;; 16-bit segment addr
  91.         DB    0            ;; high order addr
  92.         DB    &name&_ar        ;; access rights
  93.         DW    0            ;; reserved
  94.         ENDM
  95.  
  96. GATE        MACRO    export,offset,select,wc,type,priv   ;; build descriptor
  97.         IFDIF    <export>,<>        ;; check for export name
  98. export        LABEL    WORD
  99.         ENDIF
  100.         DW    offset            ;; offset
  101.         DW    select            ;; segment selector
  102.         DB    wc            ;; word count
  103.         DB    80h OR (priv SHL 5) + type    ;; access rights
  104.         DW    0            ;; reserved
  105.         ENDM
  106.  
  107. ; Selector creating macros for Task segments
  108. GDT_SEL     MACRO    sel,priv
  109.         DW    sel + priv        ;; assume sel = index * 8
  110.         ENDM
  111.  
  112. LDT_SEL     MACRO    sel,priv
  113.         DW    sel + 4 + priv        ;; like GDT but TI bit set
  114.         ENDM
  115.  
  116. ; Utility macros
  117. CALL_EX     MACRO    sel,rpl         ;; call exported item
  118.         DB    9Ah            ;; FAR call
  119.         DW    0            ;; no offset
  120.         DW    sel + rpl        ;; selector with req. priv.
  121.         ENDM
  122.  
  123. ALIGN        MACRO    bound            ;; align $ on power of 2 bounds
  124.         LOCAL    diff
  125.         diff = (($ - zero) AND (bound - 1))    ;; distance from bound
  126.         IF    diff NE 0            ;; if on bound skip
  127.         ORG    $ + (bound - diff)        ;; else adjust
  128.         ENDIF
  129.         ENDM
  130.  
  131.         PAGE
  132. ;    This segment contains the Global Descriptor Table
  133.  
  134.         MSEG    GDT,RD_WR,0
  135. ; Required by INT 15
  136.     DESCRIP <0,0,0,0>            ; GDT(0) always blank
  137.     DSCRP    int15_gdt_dat,GDT        ; DATA -> GDT
  138.     DSCRP    int15_idt_dat,IDT        ; DATA -> IDT
  139.     DSCRP    ,DSC                ; DATA -> DS
  140.     DSCRP    ,DSC                ; DATA -> ES
  141.     DSCRP    ,DSC                ; STACK -> SS
  142.     DSCRP    ,INIT                ; CODE -> CS
  143.     DESCRIP <0,0,0,0>            ; CODE -> BIOS/int 15 reserved
  144.     DSCRP    setup_tss,INIT_TSS        ; TSS -> initial task
  145. ;  Mini BIOS
  146.     DSCRP    bio_dat,MBDAT            ; DATA -> mini bios
  147.     DSCRP    bios_seg,BIOS            ; CODE -> mini bios
  148.     DSCRP    disp_mono,MONO_RAM        ; DATA -> monochrome display
  149.     DSCRP    disp_color,COLOR_RAM        ; DATA -> color display
  150. ; Fault handlers
  151.     DSCRP    task_df,FTASK8            ; TSS  -> double fault
  152. xtra8    DESCRIP <ftask8_limit,FTASK8,0,92h>    ; writable DATA alias for TSS
  153.     DSCRP    task_tf,FTASK10         ; TSS  -> task fault
  154. xtra10    DESCRIP <ftask10_limit,FTASK10,0,92h>    ; writable DATA alias for TSS
  155.     DSCRP    task_sf,FTASK12         ; TSS  -> task fault
  156. xtra12    DESCRIP <ftask12_limit,FTASK12,0,92h>    ; writable DATA alias for TSS
  157.     DSCRP    fault_dat,FDAT            ; DATA -> handler
  158.     DSCRP    fhandler,HAND            ; CODE -> handler
  159.     DSCRP    falias,FDAT            ; free for fault handler use
  160. ; Shared library
  161.     DSCRP    share_lib,SHLIB         ; CODE -> shared
  162.     GATE    share_gate,shlib_start,share_lib,0,CALL_GATE,3    ; GATE to code
  163. ; Second task
  164.     DSCRP    task2_tss,TASK2         ; TSS for 2nd task
  165.     DSCRP    task2_ldt,T2LDT         ; LDT for 2nd task
  166. ; Future use
  167.     DESCRIP <0,0,0,0>            ; available
  168.     DESCRIP <0,0,0,0>            ; available
  169.     DESCRIP <0,0,0,0>            ; available
  170.     DESCRIP <0,0,0,0>            ; available
  171.         ENDSEG    GDT
  172.  
  173.         PAGE
  174. ;    This segment contains the Interrupt Descriptor Table.
  175.  
  176.         MSEG    IDT,RD_WR,0
  177. ; Chip level interrupts (0 - 1Fh)
  178.     GATE    ,fault_00,fhandler,0,TRAP_GATE,0    ; DIVIDE
  179.     GATE    ,fault_01,fhandler,0,TRAP_GATE,0    ; TRAP
  180.     GATE    ,fault_02,fhandler,0,TRAP_GATE,0    ; NMI
  181.     GATE    ,fault_03,fhandler,0,TRAP_GATE,0    ; BRKPT
  182.     GATE    ,fault_04,fhandler,0,TRAP_GATE,0    ; INTO
  183.     GATE    ,fault_05,fhandler,0,TRAP_GATE,0    ; BOUND
  184.     GATE    ,fault_06,fhandler,0,TRAP_GATE,0    ; undef
  185.     GATE    ,fault_07,fhandler,0,TRAP_GATE,0    ; 287 NAVAIL
  186.     GATE    ,0,task_df,0,TASK_GATE,0        ; DBL FAULT
  187.     GATE    ,fault_09,fhandler,0,TRAP_GATE,0    ; 287 OVRRUN
  188.     GATE    ,0,task_tf,0,TASK_GATE,0        ; TSS FAULT
  189.     GATE    ,fault_11,fhandler,0,TRAP_GATE,0    ; NP FAULT
  190.     GATE    ,0,task_sf,0,TASK_GATE,0        ; STACK FAULT
  191.     GATE    ,fault_13,fhandler,0,TRAP_GATE,0    ; GP FAULT
  192.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  193.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  194.     GATE    ,fault_16,fhandler,0,TRAP_GATE,0    ; 287 ERROR
  195.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  196.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  197.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  198.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  199.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  200.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  201.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  202.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  203.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  204.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  205.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  206.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  207.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  208.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  209.     GATE    ,unknown,fhandler,0,TRAP_GATE,0
  210. ; System interrupts
  211. ;   Hardware Level 0 (20-27)         DOS equivalent vector
  212.     GATE    ,timer_int,bios_seg,0,INT_GATE,0    ;  8
  213.     GATE    ,kb_int,bios_seg,0,INT_GATE,0        ;  9
  214.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ;  A
  215.     GATE    ,com1_int,bios_seg,0,INT_GATE,0     ;  B
  216.     GATE    ,com2_int,bios_seg,0,INT_GATE,0     ;  C
  217.     GATE    ,prn2_int,bios_seg,0,INT_GATE,0     ;  D
  218.     GATE    ,fd_int,bios_seg,0,INT_GATE,0        ;  E
  219.     GATE    ,prn1_int,bios_seg,0,INT_GATE,0     ;  F
  220. ;  Hardware Level 1 (28-2F)
  221.     GATE    ,rtc_int,bios_seg,0,INT_GATE,0        ; 70
  222.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ; 71
  223.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ; 72
  224.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ; 73
  225.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ; 74
  226.     GATE    ,n287_int,bios_seg,0,INT_GATE,0     ; 75
  227.     GATE    ,hd_int,bios_seg,0,INT_GATE,0        ; 76
  228.     GATE    ,rsrv_int,bios_seg,0,INT_GATE,0     ; 77
  229. ; Mini BIOS (30 - 31)
  230.     GATE    ,int_30,bios_seg,0,TRAP_GATE,3
  231.     GATE    ,sw_reset,bios_seg,0,TRAP_GATE,0
  232.         ENDSEG    IDT
  233.  
  234.         PAGE
  235. ;    Mini BIOS
  236. ;      This section contains the "miniBIOS," a collection of
  237. ;      routines for hardware support, including the interrupt
  238. ;      handlers, and user-callable display routines.
  239.  
  240. ; PC/AT Hardware Control
  241.         MSEG    MONO_RAM,RD_WR,0,<AT 0B000h>
  242.         ORG    4000            ; end of monochrome RAM
  243.         ENDSEG    MONO_RAM
  244.  
  245.         MSEG    COLOR_RAM,RD_WR,0,<AT 0B800h>
  246.         ORG    16 * 1024        ; end of color RAM
  247.         ENDSEG    COLOR_RAM
  248.  
  249. MASTER        EQU    20h            ; master 8259A
  250. SLAVE        EQU    0A0h            ; slave 8259A
  251. DEV_COLOR    EQU    3D4h            ; color port
  252. RETRACE_PORT    EQU    3DAh            ; port for horiz/vert retrace
  253. DEV_MONO    EQU    3B4h            ; monochrome port
  254. DEV_RTC     EQU    70h            ; real-time-clock port
  255.  
  256. EOI        EQU    20h            ; end of interrupt command
  257.  
  258. WR_DEVICE    MACRO    device,unit,data    ; write to rtc or crt devices
  259.         IFDIF    <device>,<>
  260.         mov    dx, device
  261.         ENDIF
  262.         mov    al, unit
  263.         out    dx, al
  264.         inc    dx
  265.         mov    al, data
  266.         out    dx, al
  267.         dec    dx
  268.         ENDM
  269.  
  270.  
  271.         MSEG    MBDAT,RD_WR,0
  272.  
  273. tick_ctr    DD    0            ; incremented by timer int
  274. kb_ctr        DW    -2            ; keyboard interrupts
  275. ;    NOTE:  This program is setup to run on a monochrome system only
  276. ;    This pointer must be modified to support a color display.
  277. display_ptr    LABEL    DWORD            ; points to display RAM
  278.         DW    0                ; offset
  279.         DW    disp_mono            ; selector (MONOCHROME)
  280. cursor        LABEL    word
  281. cursor_x    DB    0            ; column
  282. cursor_y    DB    0            ; row
  283. attrib        DB    7
  284.         ENDSEG    MBDAT
  285.  
  286.         MSEG    BIOS,EXONLY,0
  287.         ASSUME    CS:BIOS, DS:NOTHING
  288.  
  289. ; INTERRUPT HANDLERS
  290. ;   This is where the MINIBIOS comes when it gets a hardware interrupt.
  291. ;   In this implementation, the only interrupt which is handled is
  292. ;   the timer tick.  The keyboard interrupt is also used as a signal
  293. ;   to exit protected mode.  The other handlers are left as an
  294. ;   exercise for the user.
  295. ; Level 0 interrupts
  296. timer_int:
  297.         push    ax
  298.         push    ds
  299.         mov    ax, OFFSET bio_dat    ; data seg selector
  300.         mov    ds, ax
  301.         ASSUME    DS:MBDAT
  302.         inc    WORD PTR tick_ctr    ; bump counter
  303.         adc    WORD PTR tick_ctr[2], 0
  304.         mov    al, EOI         ; signal 8259A
  305.         out    MASTER, al
  306.         pop    ds
  307.         ASSUME    DS:NOTHING
  308.         pop    ax
  309.         iret
  310.  
  311. kb_int:
  312.         push    ax
  313.         mov    al, EOI
  314.         out    MASTER, al
  315.         pop    ax
  316.         int    31h            ; RESET system
  317.         iret
  318.  
  319. com1_int:
  320.         push    ax
  321.         mov    al, EOI
  322.         out    MASTER, al
  323.         pop    ax
  324.         iret
  325.  
  326. com2_int:
  327.         push    ax
  328.         mov    al, EOI
  329.         out    MASTER, al
  330.         pop    ax
  331.         iret
  332.  
  333. prn2_int:
  334.         push    ax
  335.         mov    al, EOI
  336.         out    MASTER, al
  337.         pop    ax
  338.         iret
  339.  
  340. fd_int:
  341.         push    ax
  342.         mov    al, EOI
  343.         out    MASTER, al
  344.         pop    ax
  345.         iret
  346.  
  347. prn1_int:
  348.         push    ax
  349.         mov    al, EOI
  350.         out    MASTER, al
  351.         pop    ax
  352.         iret
  353.  
  354. ; Level 1 interrupts - must EOI both the SLAVE and MASTER 8259As
  355. rtc_int:
  356.         push    ax
  357.         mov    al, EOI
  358.         out    SLAVE, al
  359.         out    MASTER, al
  360.         pop    ax
  361.         iret
  362.  
  363. n287_int:
  364.         push    ax
  365.         mov    al, EOI
  366.         out    SLAVE, al
  367.         out    MASTER, al
  368.         pop    ax
  369.         iret
  370.  
  371. hd_int:
  372.         push    ax
  373.         mov    al, EOI
  374.         out    SLAVE, al
  375.         out    MASTER, al
  376.         pop    ax
  377.         iret
  378.  
  379. rsrv_int:
  380.         int    1Fh            ; cause failure
  381.  
  382.         PAGE
  383.  
  384. ; MiniBIOS user callable function codes
  385. MBIOS_WR_CHAR    EQU    0
  386. MBIOS_WR_STRING EQU    1
  387. MBIOS_WR_CRSR    EQU    2
  388. MBIOS_WR_ATTR    EQU    3
  389. MBIOS_BELL    EQU    4
  390. MBIOS_CLS    EQU    5
  391.  
  392. ; USER CALLABLE FUNCTIONS
  393. ;    INT 30h
  394. ;    Write to display    -- All registers but AX preserved
  395. ;        FN:    AH = 0        Write character
  396. ;        Input:    AL = char
  397. ;
  398. ;        FN:    AH = 1        Write ASCIIZ string
  399. ;        Input    DS:SI -> string
  400. ;
  401. ;        FN:    AH = 2        Set cursor
  402. ;        Input:    DH = row
  403. ;            DL = column
  404. ;
  405. ;        FN:    AH = 3        Set attribute
  406. ;        Input:    AL = attribute
  407. ;
  408. ;        FN:    AH = 4        Bell
  409. ;
  410. ;        FN    AH = 5        Clear Screen
  411. ;
  412. int_30:
  413.         cld
  414.         or    ah, ah        ; determine function
  415.         jz    co
  416.         dec    ah
  417.         jnz    $ + 5
  418.         jmp    linout
  419.         dec    ah
  420.         jnz    $ + 5
  421.         jmp    set_cursor
  422.         dec    ah
  423.         jnz    $ + 5
  424.         jmp    set_attrib
  425.         dec    ah
  426.         jnz    $ + 5
  427.         jmp    bell
  428.         dec    ah
  429.         jnz    $ + 5
  430.         jmp    cls
  431.         iret
  432.  
  433. wr_cursor    PROC    NEAR
  434. ; Write HW cursor - cursor in DX, trashes AX, CX, DX
  435.         mov    ax, 80            ; convert to 16 bit
  436.         mul    dh
  437.         xor    dh, dh
  438.         add    dx, ax
  439.         mov    cx, dx
  440.         WR_DEVICE DEV_MONO,14,ch    ; write hardware
  441.         WR_DEVICE ,15,cl
  442.         ret
  443. wr_cursor    ENDP
  444.  
  445. co:
  446.         push    cx            ; save state
  447.         push    di
  448.         push    ds
  449.         push    es
  450.         mov    cx, OFFSET bio_dat    ; bios data segment
  451.         mov    ds, cx
  452.         ASSUME    DS:MBDAT
  453.         les    di, display_ptr
  454.         mov    ch, al            ; save character
  455.         mov    ax, 80 * 2        ; number of columns/row
  456.         mov    cl, cursor_y        ; time #rows
  457.         mul    cl
  458.         add    di, ax            ; update offset
  459.         xor    ax, ax            ; zero
  460.         mov    al, cursor_x        ; column
  461.         shl    al, 1            ; * 2
  462.         add    di, ax            ; update offset
  463.         mov    al, ch            ; restore character
  464.         mov    ah, attrib        ; get data
  465.         stosw
  466.         inc    cursor_x        ; ajust cursor position
  467.         cmp    cursor_x, 80
  468.         jb    co_done
  469.         sub    cursor_x, 80
  470.         inc    cursor_y
  471. co_done:    push    dx
  472.         mov    dx, cursor
  473.         call    wr_cursor
  474.         pop    dx
  475.         pop    es
  476.         pop    ds
  477.         ASSUME    DS:NOTHING
  478.         pop    di
  479.         pop    cx
  480.         iret
  481.  
  482. linout:
  483.         push    es            ; save state
  484.         push    si
  485.         push    di
  486.         push    cx
  487.         push    ds
  488.         mov    cx, OFFSET bio_dat    ; bios data segment
  489.         mov    ds, cx
  490.         ASSUME    DS:MBDAT
  491.         les    di, display_ptr     ; get screen pointer
  492.         mov    ax, 80 * 2        ; number of columns/row
  493.         mov    cl, cursor_y        ; time #rows
  494.         mul    cl
  495.         add    di, ax            ; update offset
  496.         xor    ax, ax            ; zero
  497.         mov    al, cursor_x        ; column
  498.         shl    al, 1            ; * 2
  499.         add    di, ax            ; update offset
  500.         mov    ah, attrib        ; screen attribute
  501.         pop    ds            ; user data
  502.         ASSUME    DS:NOTHING
  503.         xor    cx, cx            ; count
  504.         cld
  505. linout_loop:    lodsb
  506.         or    al, al            ; end of string?
  507.         jz    line_done        ; yes - quit loop
  508.         stosw                ; no - write char/attrib
  509.         inc    cx
  510.         jmp    linout_loop        ; write next char
  511. line_done:    push    ds
  512.         mov    ax, OFFSET bio_dat    ; bios data segment
  513.         mov    ds, ax
  514.         ASSUME    DS:MBDAT
  515.         mov    ax, cx            ; count
  516.         mov    cl, 80
  517.         div    cl            ; al = rows, ah = columns
  518.         add    cursor_x, ah
  519.         cmp    cursor_x, 80        ; overflow?
  520.         jb    update_row        ; no
  521.         sub    cursor_x, 80        ; else adjust
  522.         inc    al
  523. update_row:    add    cursor_y, al
  524.         push    dx
  525.         mov    dx, cursor
  526.         call    wr_cursor
  527.         pop    dx
  528.         pop    ds
  529.         ASSUME    DS:NOTHING
  530.         pop    cx
  531.         pop    di            ; start of chars written
  532.         pop    si            ; restore state
  533.         pop    es
  534.         iret                ; and return
  535.  
  536. set_cursor:
  537.         push    cx
  538.         push    dx
  539.         push    ds
  540.         mov    ax, OFFSET bio_dat    ; bios data segment
  541.         mov    ds, ax
  542.         ASSUME    DS:MBDAT
  543.         mov    cursor, dx        ; save new cursor
  544.         call    wr_cursor
  545.         pop    ds
  546.         ASSUME    DS:NOTHING
  547.         pop    dx
  548.         pop    cx
  549.         iret
  550.  
  551. set_attrib:
  552.         push    cx
  553.         push    ds
  554.         mov    cx, OFFSET bio_dat    ; bios data segment
  555.         mov    ds, cx
  556.         ASSUME    DS:MBDAT
  557.         mov    attrib, al
  558.         pop    ds
  559.         ASSUME    DS:NOTHING
  560.         pop    cx
  561.         iret
  562.  
  563. bell:
  564.         push    ax
  565.         push    bx
  566.         push    cx
  567.         mov    bx, 200
  568.         in    al, 61h         ; get current state
  569.         push    ax            ; save it
  570. bell_loop:    and    al, 0FCh        ; speaker off
  571.         out    61h, al
  572.         mov    cx, 60
  573. idle1:        loop    idle1
  574.         or    al, 002h        ; speaker on
  575.         out    61h, al
  576.         mov    cx, 180         ; duty cycle 1:3
  577. idle2:        loop    idle2
  578.         dec    bx            ; test major loop
  579.         jnz    bell_loop
  580.         pop    ax
  581.         out    61h, al         ; restore state
  582.         pop    cx
  583.         pop    bx
  584.         pop    ax
  585.         iret
  586.  
  587. cls:
  588.         push    cx            ; save state
  589.         push    dx
  590.         push    di
  591.         push    ds
  592.         push    es
  593.         mov    cx, OFFSET bio_dat    ; bios data segment
  594.         mov    ds, cx
  595.         ASSUME    DS:MBDAT
  596.         les    di, display_ptr
  597.         mov    ah, attrib
  598.         mov    al, ' '
  599.         mov    cx, 80 * 25
  600.         cld
  601.         rep stosw
  602.         xor    dx, cx
  603.         mov    cursor, dx
  604.         call    wr_cursor
  605.         pop    es
  606.         pop    ds
  607.         pop    di
  608.         pop    dx
  609.         pop    cx
  610.         iret
  611.  
  612.  
  613. ;
  614. ;    INT 31
  615. ;    Reset processor
  616. ;
  617. sw_reset    PROC    FAR
  618.         WR_DEVICE DEV_RTC,0Fh,0     ; write SHUTDOWN code to RTC
  619.         mov    al, 0FEh        ; HW SHUTDOWN
  620.         out    64h, al         ; HW STATUS
  621. halt:        hlt
  622.         jmp    halt
  623. sw_reset    ENDP
  624.         ENDSEG    BIOS
  625.  
  626.         PAGE
  627. ; FAULT HANDLERS
  628. ;   In this prototype system, all the fault handler does is to display
  629. ;   the name and location of the fault on the screen for a short period
  630. ;   of time before resetting the system.  This should provide the user
  631. ;   with enough information to correct the problem.
  632.  
  633. ; TSS for #DF - double fault handler
  634. ; #DF must have its own task to prevent shutdown
  635.         SSEG    FTASK8,TSS,0
  636.         DW    0            ; back link
  637.         DW    0, 0            ; SS0:SP - unneeded/CPL=0
  638.         DW    0, 0            ; SS1:SP
  639.         DW    0, 0            ; SS2:SP
  640.         DW    fault_ts        ; IP
  641.         DW    0            ; flags
  642.         DW    4 DUP (0)        ; AX/CX/DX/BX
  643.         DW    fhandler_stack        ; SP
  644.         DW    fhandler_stack        ; BP
  645.         DW    msg_08, 0        ; SI/DI
  646.         GDT_SEL xtra8,0         ; ES
  647.         GDT_SEL fhandler,0        ; CS
  648.         GDT_SEL fault_dat,0        ; SS
  649.         GDT_SEL fault_dat,0        ; DS
  650.         DW    0            ; LDT selector
  651.         ENDSEG    FTASK8
  652.  
  653. ; TSS for #TF - task fault handler
  654. ; #TF must have its own task to ensure a valid machine state
  655.         SSEG    FTASK10,TSS,0
  656.         DW    0            ; back link
  657.         DW    0, 0            ; SS0:SP - unneeded/CPL=0
  658.         DW    0, 0            ; SS1:SP
  659.         DW    0, 0            ; SS2:SP
  660.         DW    fault_ts        ; IP
  661.         DW    0            ; flags
  662.         DW    4 DUP (0)        ; AX/CX/DX/BX
  663.         DW    fhandler_stack        ; SP
  664.         DW    fhandler_stack        ; BP
  665.         DW    msg_10, 0        ; SI/DI
  666.         GDT_SEL xtra10,0        ; ES
  667.         GDT_SEL fhandler,0        ; CS
  668.         GDT_SEL fault_dat,0        ; SS
  669.         GDT_SEL fault_dat,0        ; DS
  670.         DW    0            ; LDT selector
  671.         ENDSEG    FTASK10
  672.  
  673. ; TSS for #SF - stack fault handler
  674. ; #SF requires its own task to prevent #DF in certain occasions
  675.         SSEG    FTASK12,TSS,0
  676.         DW    0            ; back link
  677.         DW    0, 0            ; SS0:SP - unneeded/CPL=0
  678.         DW    0, 0            ; SS1:SP
  679.         DW    0, 0            ; SS2:SP
  680.         DW    fault_ts        ; IP
  681.         DW    0            ; flags
  682.         DW    4 DUP (0)        ; AX/CX/DX/BX
  683.         DW    fhandler_stack        ; SP
  684.         DW    fhandler_stack        ; BP
  685.         DW    msg_12, 0        ; SI/DI
  686.         GDT_SEL xtra12,0        ; ES
  687.         GDT_SEL fhandler,0        ; CS
  688.         GDT_SEL fault_dat,0        ; SS
  689.         GDT_SEL fault_dat,0        ; DS
  690.         DW    0            ; LDT selector
  691.         ENDSEG    FTASK12
  692.  
  693.         MSEG    FDAT,RD_WR,0
  694. ; Data for fault handlers
  695. msg_00        DB    "*** DIVIDE FAULT ***", 0
  696. msg_01        DB    "*** SINGLE STEP TRAP ***", 0
  697. msg_02        DB    "*** NMI ***", 0
  698. msg_03        DB    "*** INT 3 ***", 0
  699. msg_04        DB    "*** OVERFLOW EXCEPTION ***", 0
  700. msg_05        DB    "*** BOUND EXCEPTION ***", 0
  701. msg_06        DB    "*** UNDEFINED OPCODE ***", 0
  702. msg_07        DB    "*** 287 NOT AVAILABLE ***", 0
  703. msg_08        DB    "*** DOUBLE FAULT ***", 0
  704. msg_09        DB    "*** 287 SEGMENT OVERRUN ***", 0
  705. msg_10        DB    "*** ILLEGAL TSS FAULT ***", 0
  706. msg_11        DB    "*** NOT PRESENT FAULT ***", 0
  707. msg_12        DB    "*** STACK FAULT ***", 0
  708. msg_13        DB    "*** GENERAL PROTECTION FAULT ***", 0
  709. msg_16        DB    "*** 287 EXCEPTION ***", 0
  710. msg_fcode    DB    "*** Fault code = ",0
  711. msg_faddr    DB    "*** Fault address = ",0
  712. msg_unknown    DB    "*** UNKNOWN EXCEPTION ***", 0
  713. msg_buffer    DB    40 DUP (0)
  714.  
  715.         ALIGN    2            ; force stack to word boundary
  716.         DW    64 DUP (0)
  717. fhandler_stack    LABEL    WORD
  718.  
  719.         ENDSEG    FDAT
  720.  
  721.         MSEG    HAND,EXONLY,0
  722. ; Code for fault handlers
  723.         ASSUME    CS:HAND, DS:FDAT
  724.  
  725. fault_00:    mov    si, OFFSET msg_00
  726.         jmp    fail
  727.  
  728. fault_01:    mov    si, OFFSET msg_01
  729.         jmp    fail
  730.  
  731. fault_02:    mov    si, OFFSET msg_02
  732.         jmp    fail
  733.  
  734. fault_03:    mov    si, OFFSET msg_03
  735.         jmp    fail
  736.  
  737. fault_04:    mov    si, OFFSET msg_04
  738.         jmp    fail
  739.  
  740. fault_05:    mov    si, OFFSET msg_05
  741.         jmp    fail
  742.  
  743. fault_06:    mov    si, OFFSET msg_06
  744.         jmp    fail
  745.  
  746. fault_07:    mov    si, OFFSET msg_07
  747.         jmp    fail
  748.  
  749. fault_08:    mov    si, OFFSET msg_08
  750.         jmp    fail
  751.  
  752. fault_09:    mov    si, OFFSET msg_09
  753.         jmp    fail
  754.  
  755. fault_10:    mov    si, OFFSET msg_10
  756.         jmp    fail
  757.  
  758. fault_11:    mov    si, OFFSET msg_11
  759.         jmp    fail
  760.  
  761. fault_12:    mov    si, OFFSET msg_12
  762.         jmp    fail
  763.  
  764. fault_13:    mov    si, OFFSET msg_13
  765.         jmp    fail
  766.  
  767. fault_16:    mov    si, OFFSET msg_16
  768.         jmp    fail
  769.  
  770. unknown:    mov    si, OFFSET msg_unknown
  771.         jmp    fail
  772.  
  773. ; All fault handlers that have a task switch come here
  774. fault_ts:
  775.         pop    ax            ; error code
  776.         mov    bx, ES:[back_link]    ; selector of faulting task
  777.         lar    dx, bx            ; check if accessable
  778.         jnz    fake_data        ; invalid
  779.         test    dh, 80h         ; check present bit
  780.         jz    fake_data        ; invalid
  781.         and    dh, 1Fh         ; mask - leaving type info
  782.         cmp    dh, TSS_BUSY        ; should point to user TSS
  783.         jne    fake_data        ; invalid
  784.         lsl    dx, bx            ; get segment size
  785.         cmp    dx, TSS_LIMIT        ; ensure size OK
  786.         jb    fake_data        ; branch too small
  787.         ; At this point, we know that the back link points to a
  788.         ; valid TSS, we now wish to create a readable data segment
  789.         ; that points to the same physical location as the TSS so
  790.         ; we can extract some information from it.  Since this segment
  791.         ; is created pointing to the same address as a previously
  792.         ; existing segment, it is called an ALIAS
  793.         mov    di, OFFSET falias    ; offset of free descriptor
  794.         mov    dx, OFFSET int15_gdt_dat; selector for GDT as
  795.         mov    es, dx            ; if it were a data seg
  796.         and    bx, 0FFF8h        ; convert selector to offset
  797.         mov    cx, ES:[bx].phys_addr_lo; get phys addr of user TSS
  798.         mov    ES:[di].phys_addr_lo, cx; store in free descriptor
  799.         mov    cl, ES:[bx].phys_addr_hi; continue with high byte
  800.         mov    ES:[di].phys_addr_hi, cl
  801.         mov    ES:[di].limit, TSS_LIMIT; complete free descriptor
  802.         mov    es, di            ; use as selector to segment
  803.         push    ES:[rCS]        ; push task's CS
  804.         push    ES:[rIP]        ; push task's IP
  805.         push    ax            ; error code
  806.         jmp    fail
  807.  
  808.  
  809. fake_data:    push    WORD PTR 0FFFFh     ; can't get real info
  810.         push    WORD PTR 0FFFEh     ; push false CS, IP
  811.         push    ax            ; error code
  812.         jmp    fail
  813.  
  814.  
  815. pause        PROC    NEAR
  816.         mov    bx, 10
  817. ploop:        mov    cx, 0FFFFh
  818.         loop    $
  819.         dec    bx
  820.         jnz    ploop
  821.         ret
  822. pause        ENDP
  823.  
  824. fail:        mov    ax, OFFSET fault_dat    ; get legal DS
  825.         mov    ds, ax
  826.         mov    es, ax
  827.         mov    dx, 0            ; cursor x=0/y=0
  828.         mov    ah, MBIOS_WR_CRSR    ; home cursor
  829.         int    30h
  830.         mov    ah, MBIOS_WR_STRING    ; write msg
  831.         int    30h
  832.         mov    dx, 0100h        ; cursor x=0/y=1
  833.         mov    ah, MBIOS_WR_CRSR    ; home cursor
  834.         int    30h
  835.         ; check if error code on stack
  836.         cmp    si, OFFSET msg_08    ; was DF fault?
  837.         je    show_code
  838.         cmp    si, OFFSET msg_10    ; was TF fault?
  839.         je    show_code
  840.         cmp    si, OFFSET msg_11    ; was NP fault?
  841.         je    show_code
  842.         cmp    si, OFFSET msg_12    ; was SF fault?
  843.         je    show_code
  844.         cmp    si, OFFSET msg_13    ; was GP fault?
  845.         jne    show_addr
  846. show_code:    mov    si, OFFSET msg_fcode    ; print code message
  847.         mov    ah, MBIOS_WR_STRING
  848.         int    30h
  849.         pop    dx            ; get code from stack
  850.         mov    di, OFFSET msg_buffer
  851.         mov    ah, LIB_BIN_HEX     ; convert to hex
  852.         CALL_EX share_gate,0
  853.         mov    si, OFFSET msg_buffer    ; and print
  854.         mov    ah, MBIOS_WR_STRING
  855.         int    30h
  856.         mov    dx, 0200h        ; cursor x=0/y=2
  857.         mov    ah, MBIOS_WR_CRSR    ; home cursor
  858.         int    30h
  859. show_addr:
  860.         mov    si, OFFSET msg_faddr    ; print addr message
  861.         mov    ah, MBIOS_WR_STRING
  862.         int    30h
  863.         pop    bx            ; get offset
  864.         pop    dx            ; get segment
  865.         push    bx            ; save offset
  866.         mov    di, OFFSET msg_buffer
  867.         mov    ah, LIB_BIN_HEX
  868.         CALL_EX share_gate,0
  869.         mov    si, OFFSET msg_buffer
  870.         mov    ah, MBIOS_WR_STRING
  871.         int    30h
  872.         mov    al, ':'
  873.         mov    ah, MBIOS_WR_CHAR
  874.         int    30h
  875.         pop    dx            ; offset
  876.         mov    di, OFFSET msg_buffer
  877.         mov    ah, LIB_BIN_HEX
  878.         CALL_EX share_gate,0
  879.         mov    si, OFFSET msg_buffer
  880.         mov    ah, MBIOS_WR_STRING
  881.         int    30h
  882.         call    pause            ; wait
  883.         call    pause
  884.         call    pause
  885.         mov    ah, MBIOS_BELL        ; bell
  886.         int    30h
  887.         call    pause
  888.         mov    ah, MBIOS_BELL        ; bell
  889.         int    30h
  890.         call    pause
  891.         mov    ah, MBIOS_BELL        ; bell
  892.         int    30h
  893.         call    pause            ; wait
  894.         call    pause
  895.         call    pause
  896.         call    pause
  897.         int    31h            ; reset processor
  898.         ENDSEG    HAND
  899.  
  900.         PAGE
  901.         MSEG    SHLIB,EX_RD_CF,0
  902. ;
  903. ;    This segment implements a library of shared functions that may be
  904. ;    invoked through gate "share_gate".  The segment is conforming, so its
  905. ;    code will run at the same privelege as the caller.  The calling
  906. ;    sequence is merely to set up the registers and CALL the gate.  If an
  907. ;    illegal function number is called, the system issues a DIVIDE BY 0.
  908. ;    Only registers BP, SP, CS, DS, ES, and SS are guaranteed preserved.
  909. ;
  910.         ASSUME    CS:SHLIB, DS:NOTHING, ES:NOTHING
  911.  
  912. LIB_SINT_BIN    EQU    0
  913. LIB_UINT_BIN    EQU    1
  914. LIB_HEX_BIN    EQU    2
  915. LIB_BIN_SINT    EQU    3
  916. LIB_BIN_UINT    EQU    4
  917. LIB_BIN_HEX    EQU    5
  918.  
  919. shlib_code    PROC    FAR
  920.         cld                ; set direction for string fns
  921.         cmp    ah, 5            ; beyond last function?
  922.         jbe    index            ; no - do indexing
  923.         xor    ax, ax            ; zero ax
  924.         div    al            ; force divide fault
  925.  
  926. index:        mov    bl, ah            ; get FN code
  927.         xor    bh, bh            ; clear high order
  928.         shl    bx, 1            ; convert FN to index
  929.         add    bx, OFFSET table
  930.         jmp    WORD PTR CS:[bx]    ; invoke function.
  931.  
  932. table        DW    sint_bin
  933.         DW    uint_bin
  934.         DW    hex_bin
  935.         DW    bin_sint
  936.         DW    bin_uint
  937.         DW    bin_hex
  938.  
  939. ;    Function 0 / ASCII SIGNED INT to BINARY conversion
  940. ;        AH = 0
  941. ;        DS:SI -> Null terminated string of digits
  942. ;    Returns:
  943. ;        AX <- 16-bit signed integer
  944. ;        CY <- set if error
  945. ;
  946. sint_bin:
  947.         mov    cx, 10            ; multiply constant
  948.         xor    ax, ax            ; initialize accumulator
  949.         xor    dx, dx
  950.         xor    bh, bh            ; sign flag FALSE
  951.         cmp    BYTE PTR [si], '-'      ; signed?
  952.         jne    get_schar        ; no
  953.         inc    si            ; next char
  954.         inc    bh            ; set signed flag
  955. get_schar:    mov    bl, [si]        ; get input character
  956.         inc    si            ; bump ptr
  957.         or    bl, bl            ; end of string?
  958.         jz    set_sign
  959.         cmp    bl, '0'                 ; check valid
  960.         jb    err_ret
  961.         cmp    bl, '9'
  962.         ja    err_ret
  963.         sub    bl, '0'                 ; convert digit to binary
  964.         mul    cx            ; decimal shift left
  965.         add    al, bl            ; new digit
  966.         adc    ah, 0            ; propogate carry
  967.         js    err_ret         ; quit if sign overflow
  968.         adc    dx, 0
  969.         jnz    err_ret         ; quit if overflow
  970.         jmp    get_schar
  971. set_sign:    or    bh, bh            ; sign flag on
  972.         jz    done            ; no - return
  973.         neg    ax            ; else complement
  974. done:        clc                ; no error
  975.         ret
  976. err_ret:    stc                ; CY is error flag
  977.         ret
  978.  
  979. ;    Function 1 / ASCII UNSIGNED INT to BINARY conversion
  980. ;        AH = 1
  981. ;        DS:SI -> Null terminated string of digits
  982. ;    Returns:
  983. ;        AX <- 16-bit unsigned integer
  984. ;        CY <- set if error
  985. ;
  986. uint_bin:
  987.         mov    cx, 10            ; multiply constant
  988.         xor    ax, ax            ; initialize accumulator
  989.         xor    dx, dx
  990. get_uchar:    mov    bl, [si]        ; get input character
  991.         inc    si            ; bump ptr
  992.         or    bl, bl            ; end of string?
  993.         jz    done            ; yes - return
  994.         cmp    bl, '0'                 ; check valid
  995.         jb    err_ret
  996.         cmp    bl, '9'
  997.         ja    err_ret
  998.         sub    bl, '0'                 ; convert digit to binary
  999.         mul    cx            ; decimal shift left
  1000.         add    al, bl            ; new digit
  1001.         adc    ah, 0            ; propogate carry
  1002.         adc    dx, 0
  1003.         jnz    err_ret         ; quit if overflow
  1004.         jmp    get_uchar
  1005.  
  1006. ;    Function 2 / ASCII HEX to BINARY conversion
  1007. ;        AH = 2
  1008. ;        DS:SI -> Null terminated string of digits
  1009. ;    Returns:
  1010. ;        AX <- 16-bit unsigned
  1011. ;        CY <- set if error
  1012. ;
  1013. hex_bin:
  1014.         xor    dx, dx            ; init accumulator
  1015. get_hchar:    lodsb                ; get character
  1016.         or    al, al            ; last char?
  1017.         jnz    test_hchars
  1018.         mov    ax, dx
  1019.         ret                ; CY cleared by OR
  1020. test_hchars:    cmp    al, '0'                 ; check valid digit
  1021.         jb    err_ret
  1022.         cmp    al, '9'
  1023.         jbe    got_valid
  1024.         or    al, 20h         ; must be alpha - force lower
  1025.         cmp    al, 'a'                 ; check valid char
  1026.         jb    err_ret
  1027.         cmp    al, 'f'
  1028.         ja    err_ret
  1029.         sub    al, 27h         ; adjust range
  1030. got_valid:    sub    al, '0'                 ; convert digit to binary
  1031.         cmp    dx, 0FFFh        ; test overflow
  1032.         ja    err_ret
  1033.         shl    dx, 4            ; hex shift left
  1034.         add    dl, al            ; insert new digit
  1035.         jmp    get_hchar
  1036.  
  1037. ;    Function 3 / BINARY to ASCII SIGNED INT conversion
  1038. ;        AH = 3
  1039. ;        DX -> 16-bit signed
  1040. ;        ES:DI -> Buffer for ascii string
  1041. ;    Returns:
  1042. ;        Null terminated ASCII string at ES:DI
  1043. ;
  1044. bin_sint:    test    dh, 80h         ; sign bit?
  1045.         jz    bin_uint        ; no - treat as unsigned
  1046.         mov    al, '-'                 ; else write sign
  1047.         stosb
  1048.         neg    dx            ; and complement
  1049.         jmp    bin_uint
  1050.  
  1051. div_tab     DW    10000
  1052.         DW    1000
  1053.         DW    100
  1054.         DW    10
  1055.         DW    1
  1056.  
  1057. ;    Function 4 / BINARY to ASCII UNSIGNED INT conversion
  1058. ;        AH = 4
  1059. ;        DX -> 16-bit unsigned
  1060. ;        ES:DI -> Buffer for ascii string
  1061. ;    Returns:
  1062. ;        Null terminated ASCII string at ES:DI
  1063. ;
  1064. bin_uint:    mov    si, OFFSET div_tab    ; index
  1065.         xor    bx, bx            ; bh is zero suppress flag
  1066.         mov    ax, dx            ; value
  1067. u_loop:     cmp    WORD PTR CS:[si], 1    ; last divisor?
  1068.         je    u_out            ; yes - output last digit
  1069.         xor    dx, dx            ; high order zero
  1070.         div    WORD PTR CS:[si]    ; DX:AX/10^n
  1071.         or    ax, bx            ; quotient == 0 || ! suppress?
  1072.         jz    u_loop
  1073.         mov    bh, 1            ; turn off zero suppress flag
  1074.         add    al, '0'                 ; quotient always single digit
  1075.         stosb
  1076.         mov    ax, dx            ; restore AX with remainder
  1077.         inc    si
  1078.         inc    si            ; next divisor
  1079.         jmp    u_loop
  1080. u_out:        add    al, '0'                 ; last digit
  1081.         stosb
  1082.         xor    al, al            ; ASCII null
  1083.         stosb
  1084.         ret
  1085.  
  1086. ;    Function 5 / BINARY to ASCII HEX conversion
  1087. ;        AH = 5
  1088. ;        DX -> 16-bit unsigned
  1089. ;        ES:DI -> Buffer for ascii
  1090. ;    Returns:
  1091. ;        Null terminated 4 character ASCII string at ES:DI
  1092. ;
  1093. bin_hex:    mov    al, dh            ; high order byte
  1094.         shr    al, 4            ; high nybble
  1095.         add    al, '0'                 ; convert to ASCII
  1096.         cmp    al, '9'                 ; test value > '9'
  1097.         jbe    bin_h1
  1098.         add    al, 7            ; ajust alpha
  1099. bin_h1:     stosb
  1100.         mov    al, dh            ; high order byte
  1101.         and    al, 0Fh         ; low nybble
  1102.         add    al, '0'                 ; convert to ASCII
  1103.         cmp    al, '9'                 ; test value > '9'
  1104.         jbe    bin_h2
  1105.         add    al, 7            ; ajust alpha
  1106. bin_h2:     stosb
  1107.         mov    al, dl            ; low order byte
  1108.         shr    al, 4            ; high nybble
  1109.         add    al, '0'                 ; convert to ASCII
  1110.         cmp    al, '9'                 ; test value > '9'
  1111.         jbe    bin_h3
  1112.         add    al, 7            ; ajust alpha
  1113. bin_h3:     stosb
  1114.         mov    al, dl            ; low order byte
  1115.         and    al, 0Fh         ; low nybble
  1116.         add    al, '0'                 ; convert to ASCII
  1117.         cmp    al, '9'                 ; test value > '9'
  1118.         jbe    bin_h4
  1119.         add    al, 7            ; ajust alpha
  1120. bin_h4:     stosb
  1121.         xor    al, al
  1122.         stosb                ; ASCII null
  1123.         ret
  1124.  
  1125. shlib_code    ENDP
  1126.         ENDSEG    SHLIB
  1127.  
  1128.         PAGE
  1129. ;
  1130. ;    This section contains the main code and data.  We come here initially
  1131. ;    in Real Address Mode, perform necessary setup, and enter Protected
  1132. ;    Virtual Address Mode.  The data segment has combine type STACK
  1133. ;    so that the linker will initialize SS:SP.
  1134. ;
  1135.         MSEG    DSC,RD_WR,0,STACK
  1136.  
  1137. no_pm_msg    DB    '*** Unable to enter protected mode ***$'
  1138. blank_line    DB    80 DUP (' ')
  1139.         DB    0
  1140. msg        DB    'Testing',0
  1141.  
  1142.         ALIGN    2            ; force stack to word bound
  1143.         DW    100 DUP (?)        ; stack
  1144.         ENDSEG    DSC
  1145.  
  1146.         SSEG    INIT_TSS,TSS,0
  1147.         TSS_BLOCK <>            ; uninitialized
  1148.         ENDSEG    INIT_TSS
  1149.  
  1150.         MSEG    INIT,EXONLY,0
  1151.         ASSUME    CS:INIT, DS:DSC
  1152.  
  1153. adjust_addr    PROC    NEAR
  1154. ; This subroutine marches through a descriptor table to fixup 16-bit
  1155. ; segment addresses to full 24-bit physical addresses.    Since the
  1156. ; segment fixups were done by the DOS linker in Real Address Mode,
  1157. ; all we need to do is multiply by 16.    We assume the high order 8
  1158. ; bits are zero, i.e., all addresses are in the first 1Mb.
  1159. ; Called with ES:0 pointing to table, CX is number of entries.
  1160.         xor    bx, bx            ; initial offset
  1161. l1:        mov    al, ES:[bx].access    ; get access rights byte
  1162.         test    al, 10h         ; is descriptor a segment?
  1163.         jnz    got_seg         ; yes
  1164.         and    al, 0Fh         ; extract type
  1165.         cmp    al, 3            ; gate?
  1166.         ja    update_next        ; yes - skip segment adjust
  1167. got_seg:    mov    ax, ES:[bx].phys_addr_lo; get segment
  1168.         mov    dx, 16
  1169.         mul    dx            ; convert to phys addr
  1170.         mov    ES:[bx].phys_addr_lo, ax; store
  1171.         mov    ES:[bx].phys_addr_hi, dl; 24 bits
  1172. update_next:    add    bx, 8            ; incr to next descrip
  1173.         loop    l1
  1174.         ret
  1175. adjust_addr    ENDP
  1176.  
  1177. start:
  1178.         mov    ax, DSC         ; set up DS
  1179.         mov    ds, ax
  1180.         sti
  1181.  
  1182. ; When DOS created the prototype descriptors, it placed segment addresses
  1183. ; in the physical address portion of the segment descriptors.  We must
  1184. ; fix up all descirptor tables which contain segment descriptors.
  1185.         mov    ax, T2LDT
  1186.         mov    es, ax            ; point to proto LDT
  1187.         mov    cx, t2ldt_limit     ; get limit
  1188.         inc    cx            ; bump to size in bytes
  1189.         shr    cx, 3            ; convert to # entries
  1190.         call    adjust_addr
  1191.  
  1192.         mov    ax, GDT
  1193.         mov    es, ax            ; point to proto GDT
  1194.         mov    cx, gdt_limit
  1195.         inc    cx
  1196.         shr    cx, 3            ; gdt entries
  1197.         call    adjust_addr
  1198.  
  1199. ;    Now we ask the BIOS to place us in protected mode.  The BIOS requires
  1200. ;    the first 7 descriptors of the GDT to be setup as we have done.  This
  1201. ;    gives it enough information to load GDTR and IDTR and setup a new
  1202. ;    code and data segment for the calling routine.    The BIOS will also
  1203. ;    program the 8259A to our requested interrupt vectors.  Additionally, it
  1204. ;    sets up the internal AT hardware to allow addresses > 1Mb to go out
  1205. ;    over the bus (frees A20 line).
  1206.         xor    si, si            ; ES:SI -> proto GDT
  1207.         mov    bh, 20h         ; int level 1 start
  1208.         mov    bl, 28h         ; int level 2 start
  1209.         mov    ah, 89h         ; enter PM request
  1210.         mov    cx, 0FFFFh        ; idle here to ensure all
  1211.         loop    $            ; DOS keybd ints processed
  1212.         int    15h            ; BIOS call
  1213.         jnc    vm            ; successful if no CY bit
  1214.  
  1215.         mov    ah, 9            ; no - print message
  1216.         mov    dx, OFFSET no_pm_msg
  1217.         int    21h
  1218.         mov    ax, 4C01h        ; failure
  1219.         int    21h            ; exit
  1220.  
  1221. ;;; NOW IN PROCTED MODE -- INTS DISABLED
  1222.  
  1223. vm:
  1224.         mov    bp, sp            ; setup registers
  1225.         mov    ax, ds
  1226.         mov    es, ax
  1227.         mov    ax, OFFSET setup_tss    ; active task
  1228.         ltr    ax
  1229.  
  1230. pm_init_done:
  1231.         mov    ah, MBIOS_BELL        ; bell
  1232.         int    30h
  1233.         call    idle
  1234.  
  1235.         mov    ah, MBIOS_CLS        ; cls
  1236.         int    30h
  1237.  
  1238. ; Enable ints
  1239.         xor    al, al            ; no ints masked
  1240.         out    MASTER+1, al
  1241.         out    SLAVE+1, al
  1242.         sti
  1243.  
  1244. ; Print number of ticks so far
  1245. print_ticks:
  1246.         mov    ah, MBIOS_CLS        ; clear screen
  1247.         int    30h
  1248.         mov    dx, 0010h
  1249.         mov    ah, MBIOS_WR_CRSR
  1250.         int    30h
  1251.         mov    ax, OFFSET bio_dat
  1252.         mov    es, ax
  1253.         cli
  1254.         mov    dx, WORD PTR ES:tick_ctr    ; get tick counter
  1255.         mov    ax, WORD PTR ES:tick_ctr[2]
  1256.         sti
  1257.         push    dx
  1258.         call    pr_hex_word        ; print high order
  1259.         pop    ax
  1260.         call    pr_hex_word        ; print low order
  1261.         call    idle            ; pause
  1262.  
  1263.         CALL_EX task2_tss,0        ; invoke task 2
  1264.  
  1265.         call    idle
  1266.         call    idle
  1267.         mov    ah, MBIOS_BELL        ; bell
  1268.         int    30h
  1269.         jmp    print_ticks        ; loop forever
  1270.  
  1271.  
  1272. idle        PROC    NEAR
  1273.         push    bx
  1274.         push    cx
  1275.         mov    bx, 10
  1276. iloop:        mov    cx, 0FFFFh
  1277.         loop    $
  1278.         dec    bx
  1279.         jnz    iloop
  1280.         pop    cx
  1281.         pop    bx
  1282.         ret
  1283. idle        ENDP
  1284.  
  1285. pr_hex_word    PROC
  1286. ;    Print word in AX
  1287.         push    bp
  1288.         mov    bp, sp
  1289.         sub    sp, 10            ; space for string on stack
  1290.         push    ds
  1291.         pop    es            ; es = ds
  1292.         lea    di, [bp-10]        ; destination
  1293.         mov    dx, ax            ; value
  1294.         mov    ah, LIB_BIN_HEX     ; function
  1295.         CALL_EX share_gate,0        ; shared code
  1296.         lea    si, [bp-10]        ; hex string ptr
  1297.         mov    ah, MBIOS_WR_STRING    ; function
  1298.         int    30h            ; print string
  1299.         mov    sp, bp
  1300.         pop    bp
  1301.         ret
  1302. pr_hex_word    ENDP
  1303.         ENDSEG    INIT
  1304.  
  1305.         PAGE
  1306. ;    Finally, we have a small second task, which will alternate execution
  1307. ;    with the initial task.    It runs at privelege level 3, which means it
  1308. ;    has access only to its code segment, data segment, the shared library
  1309. ;    gate and INT 30h.
  1310.  
  1311.         SSEG    T2LDT,LDT,0
  1312. ; All memory segments for this task reside in a local descriptor table.
  1313.     DSCRP    task2_cs,CODE2            ; CS for 2nd task
  1314.     DSCRP    task2_dsc,DSC2            ; DS/SS for 2nd task
  1315.     DSCRP    task2_stk0,STK2_0        ; Level 0 stack for OS calls
  1316.         ENDSEG    T2LDT
  1317.  
  1318.         SSEG    TASK2,TSS,0
  1319.         DW    0            ; back link
  1320.         DW    STK2_0_limit + 1    ; SP0
  1321.         LDT_SEL task2_stk0,0        ; SS0
  1322.         DW    0, 0            ; SS1:SP
  1323.         DW    0, 0            ; SS2:SP
  1324.         DW    top            ; initial IP
  1325.         DW    0            ; flags
  1326.         DW    4 DUP (0)        ; AX/CX/DX/BX
  1327.         DW    stack2            ; SP
  1328.         DW    stack2            ; BP
  1329.         DW    2 DUP (0)        ; SI/DI
  1330.         LDT_SEL task2_dsc,3        ; ES - segments all in LDT
  1331.         LDT_SEL task2_cs,3        ; CS
  1332.         LDT_SEL task2_dsc,3        ; SS
  1333.         LDT_SEL task2_dsc,3        ; DS
  1334.         GDT_SEL task2_ldt,0        ; LDT selector
  1335.         ENDSEG    TASK2
  1336.  
  1337.         MSEG    STK2_0,RD_WR,0
  1338.         DW    128 DUP (?)        ; stack for level 0 execution
  1339.         ENDSEG    STK2_0
  1340.  
  1341.         MSEG    DSC2,RD_WR,3
  1342. ; Data and stack segment for 2nd task
  1343. t2_msg        DB    "Task 2 running",0
  1344.  
  1345.         ALIGN    2            ; stack on word boundary
  1346.         DW    128 DUP (?)
  1347. stack2        LABEL    WORD
  1348.  
  1349.         ENDSEG    DSC2
  1350.  
  1351.         MSEG    CODE2,EXONLY,3
  1352.         ASSUME    CS:CODE2, DS:DSC2
  1353. top:                        ; task starts here first time
  1354.         mov    dx, 0032h
  1355.         mov    ah, MBIOS_WR_CRSR
  1356.         int    30h            ; cursor to "safe" location
  1357.         mov    si, OFFSET t2_msg
  1358.         mov    ah, MBIOS_WR_STRING
  1359.         int    30h            ; print message
  1360.         iret                ; return to previous task
  1361.         jmp    top            ; when task invoked again,
  1362.                         ; CS:IP points here (after IRET)
  1363.         ENDSEG    CODE2
  1364.  
  1365.         END    start
  1366.